/*!
 * @file shader.hh
 * @brief Shader
 */

#ifndef __ENGINE_BASIC_SHADER_HH
#define __ENGINE_BASIC_SHADER_HH

#include <string>
#include <fstream>
#include <list>
#include <utility>

#include "engine/gfx/moduleGraphic.hh"

/*!
 * @brief Typ shadera.
 * ST_V = vertex shader
 * ST_F = fragment shader
 */
enum _shader_type {
	ST_V, ST_F
};

/*!
 * @brief Typ zmiennej uniform.
 * SUT_[ilość argumentów][typ (int/float)]
 */
enum _shader_uniform_type {
	SUT_1, SUT_2, SUT_3, SUT_4, SUT_C
};

/*!
 * @brief Klasa pomocnicza. Zawiera pulę wskaźników do wykorzystania.
 */
template<typename TYPE>
class shaderUniform {
public:
	shaderUniform(std::string name, _shader_uniform_type type) :
		name(name), type(type), a(), b(), c(), d() {
	}

	/*!
	 * @brief Nazwa zmiennej typu Uniform
	 */
	const std::string name;

	/*!
	 * @brief Typ zmiennej Uniform
	 */
	const _shader_uniform_type type;

	/*!
	 * @brief Wskaźniki na float
	 */
	TYPE a;

	/*!
	 * @brief Wskaźniki na float
	 */
	TYPE b;

	/*!
	 * @brief Wskaźniki na float
	 */
	TYPE c;

	/*!
	 * @brief Wskaźniki na float
	 */
	TYPE d;

private:
};

/*!
 * @brief Klasa do zarządzania shaderami.
 */
class shader {
public:
	/*!
	 * @brief Konstruktor. Tworzy pusty shader.
	 * @return Obiekt.
	 */
	shader(std::string name);

	/*!
	 * @brief Dodawanie nowego programu do obiektu.
	 * @param path Adres pliku źródłowego.
	 * @param type Typ shadera
	 */
	void addShader(std::string path, _shader_type type);

	/*!
	 * @brief Dodanie zminnej typu Uniform do shadera. Jest to pula wskaźników z których
	 * odczytywane są wartości i przekazywane do shadera. Możliwe staje się ich użycie w
	 * programach cieniowania.
	 * Typ = float.
	 * @param name Nazwa zmiennej uniform
	 * @param a pierwszy wskaźnik
	 * @param b drugi wskaźnik
	 * @param c trzeci wskaźnik
	 * @param d czwarty wskaźnik
	 */
	void addUniformf(std::string name, float *a, float *b = NULL, float *c =
			NULL, float *d = NULL);

	/*!
	 * @brief Dodanie zminnej typu Uniform do shadera. Zmienna ta nie może być zmieniana.
	 * W shaderze zawsze jest typu vec4
	 * Typ = float.
	 * @param name Nazwa zmiennej uniform
	 * @param a pierwszy
	 * @param b drugi
	 * @param c trzeci
	 * @param d czwarty
	 */
	void addUniformf(std::string name, float a, float b = 0.0f, float c = 0.0f,
			float d = 0.0f);

	/*!
	 * @brief Dodanie zminnej typu Uniform do shadera. Jest to pula wskaźników z których
	 * odczytywane są wartości i przekazywane do shadera. Możliwe staje się ich użycie w
	 * programach cieniowania.
	 * Typ = int.
	 * @param name Nazwa zmiennej uniform
	 * @param a pierwszy wskaźnik
	 * @param b drugi wskaźnik
	 * @param c trzeci wskaźnik
	 * @param d czwarty wskaźnik
	 */
	void addUniformi(std::string name, int *a, int *b = NULL, int *c = NULL,
			int *d = NULL);

	/*!
	 * @brief Dodanie zminnej typu Uniform do shadera. Zmienna ta nie może być zmieniana.
	 * W shaderze zawsze jest typu vec4
	 * Typ = int.
	 * @param name Nazwa zmiennej uniform
	 * @param a pierwszy
	 * @param b drugi
	 * @param c trzeci
	 * @param d czwarty
	 */
	void addUniformi(std::string name, int a, int b = 0, int c = 0, int d = 0);

	void addTexture(std::string name, texture *tx);
	void addAttrib(std::string name, int id);


	/*!
	 * @brief Linkowanie programu. Konieczne (1 raz.) do wykonania przed
	 * wywołaniem metody bind()
	 */
	void linkProgram();

	/*!
	 * @brief Zwraca log_add będący sumą wszystkich programów cieniowania podpiętych do
	 * tego obiektu.
	 * @return Log
	 */
	std::string getLog() const;

	/*!
	 * @brief Zwraca uchwyt do programu cieniowania.
	 * @return Uchwyt do programu cieniowania.
	 */
	GLint getProgram() const;

	/*!
	 * @brief Uruchomienie programu cieniowania i przekazanie wszysktkich zmiennych
	 * typu Uniform
	 */
	void bind();

	/*!
	 * @brief Zatrzymanie programu cieniowania.
	 */
	void unbind();

private:
	/*!
	 * @brief Lista par shaderów [adres pliku + uchwyt na shader]
	 */
	std::list<std::pair<std::string, GLuint> > shadersList;

	/*!
	 * @brief Lista zmiennych typu uniform.
	 */
	std::list<shaderUniform<float*>*> listFloatPtr;
	std::list<shaderUniform<float>*> listFloat;
	std::list<shaderUniform<int*>*> listIntPtr;
	std::list<shaderUniform<int>*> listInt;

	std::list<std::pair<std::string, texture*> > listTextures;

	/*!
	 * @brief Program cieniowania.
	 */
	GLint programHandle;

	const std::string name;
};

#else
class shader;
#endif
